#pragma once

#include <vector>
#include <semaphore.h>
#include <cassert>

#include <pthread.h>

namespace ringqueue
{
    using namespace std;

    template <class T>
    class ringQueue
    {
    private:
#define MAXCAP 5
    public:
        ringQueue(const size_t &cap = MAXCAP)
            : _queue(cap), _cap(cap),_consumerPos(0),_productorPos(0)
        {
            /*消费者信号量初始化为0
             *生产者信号量初始化为队列的容量上限*/
            sem_init(&_dataSem,0,0);
            sem_init(&_spaceSem,0,_cap);
            pthread_mutex_init(&_productorMutex,nullptr);
            pthread_mutex_init(&_consumerMutex,nullptr);
        }

        ~ringQueue()
        {
            sem_destroy(&_dataSem);
            sem_destroy(&_spaceSem);
            pthread_mutex_destroy(&_productorMutex);
            pthread_mutex_destroy(&_consumerMutex);
        }

        void push(const T& in)
        {
            /*生产者生产数据之前，先申请信号量*/
            P(_spaceSem);   

            /*生产者之间互斥地访问临界资源*/
            pthread_mutex_lock(&_productorMutex);
            _queue[_productorPos++] = in;
            _productorPos %= _cap;
            pthread_mutex_unlock(&_productorMutex);
            /*生产者生产数据之后，++消费者的信号量*/
            V(_dataSem);
        }

        void pop(T *out)
        {
            /*消费者消费数据之前，先申请信号量*/
            P(_dataSem);

            /*消费者之间互斥地访问临界资源*/
            pthread_mutex_lock(&_consumerMutex);
            *out = _queue[_consumerPos++];
            _consumerPos %= _cap;
            pthread_mutex_unlock(&_consumerMutex);
            V(_spaceSem);
        }
    private:
        /*封装P、V操作*/
        void P(sem_t &sem)
        {
            int n = sem_wait(&sem);
            assert(n != -1);
            (void)n;
        }

        void V(sem_t &sem)
        {
            int n = sem_post(&sem);
            assert(n != -1);
            (void)n;
        }

    private:
        vector<T> _queue; /*用数组模拟环形队列*/

        /*环形队列的容量上限*/
        size_t _cap;

        /*消费者关注有效数据，生产者关注空闲位置
         *故定义两个信号量
         *信号量只能保证生产者与消费者的互斥与同步
         *要保证生产者与生产者、消费者与消费者之间的互斥还需要互斥锁*/
        sem_t _dataSem;
        sem_t _spaceSem;

        pthread_mutex_t _productorMutex;
        pthread_mutex_t _consumerMutex;

        /*消费者、生产者访问的下标位置*/
        int _consumerPos;
        int _productorPos;
    };

} /*namespace ringqueue ends here*/